import { FC, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { ToobarConfig, toobarItem } from './config';
import classnames from 'classnames';
import { useFocused, useSlate } from 'slate-react';
import { Editor } from 'slate';
import styles from './Editor.module.less';
import ColorPicker from './ColorPicker';
import Align from './Align';

const TOOBAR_OFFSET_HEIGHT = 28;
const TOOBAR_OFFSET_WIDTH = 49;

const HoveringToolbar: FC = () => {
  const main = useRef<HTMLDivElement | null>(null);
  const focused = useFocused(); // 当前编辑器是否是聚焦状态
  const editor = useSlate();
  const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);

  useEffect(() => {
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    const target = main.current!;
    function mousedown() {
      target.style.setProperty('display', 'none');
    }
    function mouseup() {
      const domSelection = window.getSelection();
      if (!domSelection) return;
      target.style.setProperty('display', '');
      // 获取当前范围所选内容的marks格式
      const marks: Record<string, unknown> = Editor.marks(editor) || {};
      setSelectedRowKeys(Object.keys(marks));
    }

    // 灾难始终慢我一步
    function selectionchange() {
      const domSelection = window.getSelection();
      // 此处的判断逻辑之前是采用的 slate 的 Editor.string(editor,selection) 获取选中的内容
      // 但是发现他在监听事件中永远慢一步,并且按住 shift + 鼠标单击选中内容后获取不到内容
      // 最后还是考虑使用 JS 的 window.getSelection() 来实现
      if (target && focused && domSelection && !domSelection.isCollapsed) {
        // 设置坐标
        const domRange = domSelection?.getRangeAt(0);
        const rect = domRange?.getBoundingClientRect();
        // 选中的内容
        // console.log(domSelection.toString());
        if (!rect) return;
        /**
         * rect.top: 返回选中的范围区域距离页面顶部的距离
         * window.pageYOffset: 返回文档/页面垂直方向滚动的像素值。
         * target-offsetHeight: 返回节点的高度
         * **/

        const top = rect.top + window.pageYOffset - TOOBAR_OFFSET_HEIGHT - 10;
        const left = rect.left + window.pageXOffset - TOOBAR_OFFSET_WIDTH / 2 + rect.width / 2;
        target.style.setProperty('--hover-top', top + 'px');
        target.style.setProperty('--hover-left', left + 'px');
      } else {
        target.style.setProperty('--hover-top', -10000 + 'px');
        target.style.setProperty('--hover-left', -10000 + 'px');
      }
    }

    window.addEventListener('mousedown', mousedown);
    window.addEventListener('mouseup', mouseup);
    document.addEventListener('selectionchange', selectionchange);
    return () => {
      window.removeEventListener('mousedown', mousedown);
      window.removeEventListener('mouseup', mouseup);
      document.removeEventListener('selectionchange', selectionchange);
    };
  });

  const menuChange = (record: toobarItem) => {
    const value = record.value;
    const isMark = record.mark;

    const findValue = selectedRowKeys.find((o) => o === value);
    if (findValue) {
      setSelectedRowKeys((origin) => origin.filter((o) => o !== value));
      isMark && Editor.removeMark(editor, value);
    } else {
      setSelectedRowKeys((origin) => [...origin, value]);
      isMark && Editor.addMark(editor, value, record.fill);
    }
  };

  return createPortal(
    <div
      className={styles['hovering-toolbar']}
      ref={main}
      onMouseDown={(e) => {
        e.preventDefault();
        e.stopPropagation();
      }}
    >
      <ul className={styles['toobar-ul']}>
        {ToobarConfig.map((item) => {
          return (
            <li
              className={classnames([styles['toobar-item'], styles['toobar-item-hover']], {
                [styles['active']]: selectedRowKeys.find((o) => o === item.value),
              })}
              onClick={() => menuChange(item)}
              key={item.id}
            >
              <span title={item.title} dangerouslySetInnerHTML={{ __html: item.html }}></span>
            </li>
          );
        })}
        <ColorPicker toobarDom={main.current} type={'color'} />
        <ColorPicker toobarDom={main.current} type={'backgroundColor'} />
        <Align toobarDom={main.current} />
      </ul>
    </div>,
    document.body
  );
};

export default HoveringToolbar;
